{
 "cells": [
  {
   "cell_type": "code",
   "id": "initial_id",
   "metadata": {
    "collapsed": true
   },
   "source": [
    "%load_ext autoreload\n",
    "%autoreload 2"
   ],
   "outputs": [],
   "execution_count": null
  },
  {
   "metadata": {},
   "cell_type": "code",
   "source": [
    "import os\n",
    "import numpy as np\n",
    "import torch\n",
    "import json\n",
    "import viser.transforms as vt\n",
    "from internal.dataparsers.matrix_city_dataparser import MatrixCity"
   ],
   "id": "ba4e88da621fe326",
   "outputs": [],
   "execution_count": null
  },
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": "#",
   "id": "affedbd0728c1008"
  },
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": [
    "The up direction of the MatrixCity is +z, while MegaNeRF requires -x.\n",
    "  \n",
    "So rotate the cameras $-\\pi/2$ (clockwise, 90 degrees) alone the y-axis.\n",
    "\n",
    "Here builds the rotation matrix."
   ],
   "id": "b5474839d496950d"
  },
  {
   "metadata": {},
   "cell_type": "code",
   "source": [
    "y_clockwise_rotation_half_pi = torch.eye(4, dtype=torch.double)\n",
    "y_clockwise_rotation_half_pi[:3, :3] = torch.from_numpy(vt.SO3.from_y_radians(-np.pi / 2).as_matrix())\n",
    "y_clockwise_rotation_half_pi"
   ],
   "id": "3978c29aca49987a",
   "outputs": [],
   "execution_count": null
  },
  {
   "metadata": {},
   "cell_type": "code",
   "source": [
    "dataset_path = os.path.expanduser(\"~/data-fast/MatrixCity/small_city-self_render/aerial\")\n",
    "converted_output_path = os.path.expanduser(\"~/data-fast/MatrixCity/aerial-meganerf/block_1_and_2\")"
   ],
   "id": "4174165da70bce83",
   "outputs": [],
   "execution_count": null
  },
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": [
    "# dataparser\n",
    "Load MatrixCity dataset"
   ],
   "id": "8341b69cbef0aed4"
  },
  {
   "metadata": {},
   "cell_type": "code",
   "source": [
    "dataparser = MatrixCity(\n",
    "    train=[\n",
    "        \"block_1/transforms.json\",\n",
    "        \"block_2/transforms.json\",\n",
    "    ],\n",
    "    test=[\n",
    "        \"block_1_test/transforms.json\",\n",
    "        \"block_2_test/transforms.json\",\n",
    "    ],\n",
    ").instantiate(dataset_path, os.getcwd(), 0)\n",
    "dataparser"
   ],
   "id": "b575cac530d176e7",
   "outputs": [],
   "execution_count": null
  },
  {
   "metadata": {},
   "cell_type": "code",
   "source": [
    "dataparser_outputs = dataparser.get_outputs()\n",
    "dataparser_outputs"
   ],
   "id": "22d5b70dcafa58c8",
   "outputs": [],
   "execution_count": null
  },
  {
   "metadata": {},
   "cell_type": "code",
   "source": "dataparser_outputs.train_set.image_names[0], len(dataparser_outputs.train_set.image_names)",
   "id": "9a82340a224e7ddd",
   "outputs": [],
   "execution_count": null
  },
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": "Convert poses to y up, z back",
   "id": "cdc3f2208ee75110"
  },
  {
   "metadata": {},
   "cell_type": "code",
   "source": [
    "train_c2w_from_dataparser = torch.linalg.inv(dataparser_outputs.train_set.cameras.world_to_camera.transpose(1, 2).to(torch.double))\n",
    "train_c2w_from_dataparser[:, :3, 1:3] *= -1\n",
    "train_c2w_from_dataparser[0]"
   ],
   "id": "a0301134507ffaa7",
   "outputs": [],
   "execution_count": null
  },
  {
   "metadata": {},
   "cell_type": "code",
   "source": [
    "test_c2w_from_dataparser = torch.linalg.inv(dataparser_outputs.test_set.cameras.world_to_camera.transpose(1, 2).to(torch.double))\n",
    "test_c2w_from_dataparser[:, :3, 1:3] *= -1\n",
    "test_c2w_from_dataparser[0]"
   ],
   "id": "1256119758a8e8da",
   "outputs": [],
   "execution_count": null
  },
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": "Rotate the camera poses",
   "id": "fe34b9449bc2b53a"
  },
  {
   "metadata": {},
   "cell_type": "code",
   "source": [
    "rotated_train_c2ws = y_clockwise_rotation_half_pi @ train_c2w_from_dataparser\n",
    "rotated_train_c2ws[0]"
   ],
   "id": "2dbd326890ad7c44",
   "outputs": [],
   "execution_count": null
  },
  {
   "metadata": {},
   "cell_type": "code",
   "source": [
    "rotated_test_c2ws = y_clockwise_rotation_half_pi @ test_c2w_from_dataparser\n",
    "rotated_test_c2ws[0]"
   ],
   "id": "6796c67b0339b2ac",
   "outputs": [],
   "execution_count": null
  },
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": [
    "# json\n",
    "Just for validating the poses loaded from dataparser are correct.\n",
    "  \n",
    "This section can be ignored."
   ],
   "id": "d442a0595b967c86"
  },
  {
   "metadata": {},
   "cell_type": "code",
   "source": [
    "with open(os.path.expanduser(\"~/data-extra/MatrixCity/small_city/aerial/pose/block_A/transforms_train.json\"), \"r\") as f:\n",
    "    transforms = json.load(f)\n",
    "transforms.keys()"
   ],
   "id": "578763e589fb8cfc",
   "outputs": [],
   "execution_count": null
  },
  {
   "metadata": {},
   "cell_type": "code",
   "source": "transforms[\"frames\"][0]",
   "id": "b8b56cd92fda421e",
   "outputs": [],
   "execution_count": null
  },
  {
   "metadata": {},
   "cell_type": "code",
   "source": [
    "train_c2ws_gt = torch.tensor([i[\"transform_matrix\"] for i in transforms[\"frames\"]], dtype=torch.double)\n",
    "train_c2ws_gt.shape, train_c2ws_gt[0]"
   ],
   "id": "c2f557584c348a63",
   "outputs": [],
   "execution_count": null
  },
  {
   "metadata": {},
   "cell_type": "code",
   "source": [
    "# validate that the conversion from dataparser is correct\n",
    "torch.allclose(train_c2ws_gt[0], train_c2w_from_dataparser[0]), torch.abs(train_c2ws_gt - train_c2w_from_dataparser).max()"
   ],
   "id": "3359c2c26de9ff61",
   "outputs": [],
   "execution_count": null
  },
  {
   "metadata": {},
   "cell_type": "code",
   "source": [
    "rotated_from_json = (y_clockwise_rotation_half_pi @ train_c2ws_gt)\n",
    "(rotated_from_json - rotated_train_c2ws).max(), torch.allclose(rotated_from_json, rotated_train_c2ws, atol=1e-7)"
   ],
   "id": "fed7b18a7ca11cb0",
   "outputs": [],
   "execution_count": null
  },
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": "## calculate some required info",
   "id": "2aecd6621c935be2"
  },
  {
   "metadata": {},
   "cell_type": "code",
   "source": [
    "camera_centers = rotated_train_c2ws[:, :3, 3]\n",
    "torch.min(camera_centers, dim=0).values, torch.max(camera_centers, dim=0).values"
   ],
   "id": "f2f1f94c252162da",
   "outputs": [],
   "execution_count": null
  },
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": "Take the middle of the min and max as the origin",
   "id": "1a708478d6de474c"
  },
  {
   "metadata": {},
   "cell_type": "code",
   "source": [
    "origin = (torch.max(camera_centers, dim=0).values + torch.min(camera_centers, dim=0).values) * 0.5\n",
    "origin"
   ],
   "id": "6d1ddfe619803046",
   "outputs": [],
   "execution_count": null
  },
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": "Rotate the points",
   "id": "2ee528a4b1cf5950"
  },
  {
   "metadata": {},
   "cell_type": "code",
   "source": [
    "rotated_point_xyzs = torch.from_numpy(dataparser_outputs.point_cloud.xyz).to(\n",
    "    torch.double) @ y_clockwise_rotation_half_pi[:3, :3].T\n",
    "rotated_point_xyzs.shape"
   ],
   "id": "ce4a601d9bbdcd2",
   "outputs": [],
   "execution_count": null
  },
  {
   "metadata": {},
   "cell_type": "code",
   "source": [
    "# ray_altitude_range is the x\n",
    "torch.min(rotated_point_xyzs, dim=0).values, torch.max(rotated_point_xyzs, dim=0).values"
   ],
   "id": "a7638f5197584cb0",
   "outputs": [],
   "execution_count": null
  },
  {
   "metadata": {},
   "cell_type": "code",
   "source": [
    "# Scene extent\n",
    "torch.max(rotated_point_xyzs, dim=0).values - torch.min(rotated_point_xyzs, dim=0).values"
   ],
   "id": "d660b35ea3a8ec3d",
   "outputs": [],
   "execution_count": null
  },
  {
   "metadata": {},
   "cell_type": "code",
   "source": [
    "ray_altitude_range = torch.stack([torch.min(rotated_point_xyzs, dim=0).values[0], torch.max(rotated_point_xyzs, dim=0).values[0]])\n",
    "ray_altitude_range"
   ],
   "id": "2045b636daeaa51d",
   "outputs": [],
   "execution_count": null
  },
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": [
    "## move and rescale cameras\n",
    "map camera centers to to [-1, 1]"
   ],
   "id": "7cda141260a0f98"
  },
  {
   "metadata": {},
   "cell_type": "code",
   "source": [
    "camera_centers_moved = camera_centers - origin[None, :]\n",
    "torch.min(camera_centers_moved, dim=0).values, torch.max(camera_centers_moved, dim=0).values"
   ],
   "id": "138e850ea616eea6",
   "outputs": [],
   "execution_count": null
  },
  {
   "metadata": {},
   "cell_type": "code",
   "source": [
    "scale = torch.max(camera_centers_moved)\n",
    "scale"
   ],
   "id": "81d973e90ff65424",
   "outputs": [],
   "execution_count": null
  },
  {
   "metadata": {},
   "cell_type": "code",
   "source": [
    "# just take a number looks more comfortable\n",
    "scale = torch.tensor(4.5, dtype=torch.float64)"
   ],
   "id": "e2ca14c6020c1d36",
   "outputs": [],
   "execution_count": null
  },
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": "Update the camera centers of c2w matrix",
   "id": "6f0ae158fb567cb5"
  },
  {
   "metadata": {},
   "cell_type": "code",
   "source": [
    "recentered_and_scaled_train_c2ws = torch.clone(rotated_train_c2ws)\n",
    "recentered_and_scaled_train_c2ws[:, :3, 3] -= origin[None, :]\n",
    "recentered_and_scaled_train_c2ws[:, :3, 3] /= scale\n",
    "\n",
    "recentered_and_scaled_test_c2ws = torch.clone(rotated_test_c2ws)\n",
    "recentered_and_scaled_test_c2ws[:, :3, 3] -= origin[None, :]\n",
    "recentered_and_scaled_test_c2ws[:, :3, 3] /= scale"
   ],
   "id": "e515e523e89dc364",
   "outputs": [],
   "execution_count": null
  },
  {
   "metadata": {},
   "cell_type": "code",
   "source": [
    "camera_center_extent = torch.max(recentered_and_scaled_train_c2ws[:, :3, 3], dim=0).values - torch.min(\n",
    "    recentered_and_scaled_train_c2ws[:, :3, 3], dim=0).values\n",
    "camera_center_extent, torch.all(camera_center_extent <= 2.)"
   ],
   "id": "d9e0ade0661a4f61",
   "outputs": [],
   "execution_count": null
  },
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": "# save",
   "id": "d59a7b8e17a7670f"
  },
  {
   "metadata": {},
   "cell_type": "code",
   "source": [
    "output_path = os.path.expanduser(converted_output_path)\n",
    "os.makedirs(output_path, exist_ok=True)\n",
    "output_path"
   ],
   "id": "ed6bb8f4b97d5e85",
   "outputs": [],
   "execution_count": null
  },
  {
   "metadata": {},
   "cell_type": "code",
   "source": [
    "torch.save({\n",
    "    \"origin_drb\": origin,\n",
    "    \"pose_scale_factor\": scale.item(),\n",
    "}, os.path.join(output_path, \"coordinates.pt\"))"
   ],
   "id": "b0f8534b4a1ef3b5",
   "outputs": [],
   "execution_count": null
  },
  {
   "metadata": {},
   "cell_type": "code",
   "source": [
    "def save_image_set(target_image_set, target_c2ws, target_split, idx_offset: int):\n",
    "    rgb_dir = os.path.join(output_path, target_split, \"rgbs\")\n",
    "    metadata_dir = os.path.join(output_path, target_split, \"metadata\")\n",
    "    \n",
    "    for i in os.scandir(rgb_dir):\n",
    "        if not i.is_dir(follow_symlinks=False):\n",
    "            os.unlink(i.path)\n",
    "    for i in os.scandir(metadata_dir):\n",
    "        if not i.is_dir(follow_symlinks=False):\n",
    "            os.unlink(i.path)\n",
    "    \n",
    "    os.makedirs(rgb_dir, exist_ok=True)\n",
    "    os.makedirs(metadata_dir, exist_ok=True)\n",
    "    for idx in range(len(target_image_set)):\n",
    "        name_idx = idx + idx_offset\n",
    "        os.link(target_image_set.image_paths[idx], os.path.join(rgb_dir, \"{:06d}.png\".format(name_idx)))\n",
    "        torch.save({\n",
    "            'H': target_image_set.cameras.height[idx].int().item(),\n",
    "            'W': target_image_set.cameras.width[idx].int().item(),\n",
    "            'c2w': target_c2ws[idx].to(torch.float)[:3],\n",
    "            'intrinsics': torch.tensor([\n",
    "                target_image_set.cameras.fx[idx],\n",
    "                target_image_set.cameras.fy[idx],\n",
    "                target_image_set.cameras.cx[idx],\n",
    "                target_image_set.cameras.cy[idx],\n",
    "            ]),\n",
    "        }, os.path.join(metadata_dir, \"{:06d}.pt\".format(name_idx)))"
   ],
   "id": "ff66525b6a8fc41b",
   "outputs": [],
   "execution_count": null
  },
  {
   "metadata": {},
   "cell_type": "code",
   "source": [
    "save_image_set(\n",
    "    dataparser_outputs.train_set,\n",
    "    recentered_and_scaled_train_c2ws,\n",
    "    \"train\",\n",
    "    0,\n",
    ")\n",
    "save_image_set(\n",
    "    dataparser_outputs.test_set,\n",
    "    recentered_and_scaled_test_c2ws,\n",
    "    \"val\",\n",
    "    len(dataparser_outputs.train_set),\n",
    ")"
   ],
   "id": "b60991f95ea1771c",
   "outputs": [],
   "execution_count": null
  },
  {
   "metadata": {},
   "cell_type": "code",
   "source": "torch.load(os.path.join(output_path, \"train\", \"metadata\", \"{:06d}.pt\".format(256)))",
   "id": "a033fec857aff53b",
   "outputs": [],
   "execution_count": null
  },
  {
   "metadata": {},
   "cell_type": "code",
   "source": [
    "# preview transformed ray_altitude_range (you should use the value of `ray_altitude_range` in config file)\n",
    "ray_altitude_range, (ray_altitude_range - origin[0]) / scale"
   ],
   "id": "c83559cd6c9b818a",
   "outputs": [],
   "execution_count": null
  },
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": "# Preview",
   "id": "abbd8caeb713c924"
  },
  {
   "metadata": {},
   "cell_type": "code",
   "source": [
    "from internal.utils.graphics_utils import store_ply\n",
    "camera_list = []\n",
    "for image_index, c2w in enumerate(recentered_and_scaled_train_c2ws):\n",
    "    camera_list.append({\n",
    "        \"id\": image_index,\n",
    "        \"img_name\": \"{:06d}\".format(image_index),\n",
    "        \"width\": 1920,\n",
    "        \"height\": 1080,\n",
    "        \"position\": (c2w[:3, 3].numpy() * scale.item()).tolist(),\n",
    "        \"rotation\": c2w[:3, :3].numpy().tolist(),\n",
    "        \"fx\": 1600,\n",
    "        \"fy\": 1600,\n",
    "        \"color\": [255, 0, 0],\n",
    "    })\n",
    "\n",
    "preview_json_path = os.path.join(output_path, \"preview.json\")\n",
    "with open(preview_json_path, \"w\") as f:\n",
    "    json.dump(camera_list, f)\n",
    "\n",
    "preview_ply_path = os.path.join(output_path, \"preview.ply\")\n",
    "store_ply(preview_ply_path, (rotated_point_xyzs - origin).numpy(), dataparser_outputs.point_cloud.rgb)\n",
    "    \n",
    "\"python utils/show_cameras.py --cameras {} --points {}\".format(preview_json_path, preview_ply_path)"
   ],
   "id": "952e33f3237d2b09",
   "outputs": [],
   "execution_count": null
  },
  {
   "metadata": {},
   "cell_type": "markdown",
   "source": [
    "# convert to colmap\n",
    "The conversion above should work.\n",
    "\n",
    "Converting to colmap just for validating the conversion outputs.\n",
    "\n",
    "The outputs of `colmap_to_mega_nerf.py` and above should be identical."
   ],
   "id": "9f515f87ba64dcc3"
  },
  {
   "metadata": {},
   "cell_type": "code",
   "source": "from internal.utils import colmap",
   "id": "59751c09e2bf9d2e",
   "outputs": [],
   "execution_count": null
  },
  {
   "metadata": {},
   "cell_type": "code",
   "source": [
    "colmap_output_path = os.path.join(output_path, \"colmap\")\n",
    "colmap_output_path"
   ],
   "id": "f653324743e7a829",
   "outputs": [],
   "execution_count": null
  },
  {
   "metadata": {},
   "cell_type": "code",
   "source": [
    "idx = 0\n",
    "colmap_image_path = os.path.join(colmap_output_path, \"images\")\n",
    "os.makedirs(colmap_image_path, exist_ok=True)\n",
    "\n",
    "for i in os.scandir(colmap_image_path):\n",
    "    if not i.is_dir():\n",
    "        os.unlink(i.path)\n",
    "\n",
    "colmap_image_name_to_c2w = {}\n",
    "for image_idx, i in enumerate(dataparser_outputs.train_set.image_paths):\n",
    "    colmap_image_name = \"{:06d}.png\".format(idx)\n",
    "    colmap_image_name_to_c2w[colmap_image_name] = rotated_train_c2ws[image_idx]\n",
    "    os.link(i, os.path.join(colmap_image_path, colmap_image_name))\n",
    "    idx += 1\n",
    "\n",
    "for image_idx, i in enumerate(dataparser_outputs.test_set.image_paths):\n",
    "    colmap_image_name = \"{:06d}.png\".format(idx)\n",
    "    colmap_image_name_to_c2w[colmap_image_name] = rotated_test_c2ws[image_idx]\n",
    "    os.link(i, os.path.join(colmap_image_path, colmap_image_name))\n",
    "    idx += 1\n",
    "    \n",
    "len(colmap_image_name_to_c2w)"
   ],
   "id": "d4961ae06290c8af",
   "outputs": [],
   "execution_count": null
  },
  {
   "metadata": {},
   "cell_type": "code",
   "source": [
    "colmap_db_path = os.path.join(colmap_output_path, \"colmap.db\")\n",
    "print(\" \\\\\\n    \".join([\n",
    "    \"colmap\",\n",
    "    \"feature_extractor\",\n",
    "    \"--database_path=\" + colmap_db_path,\n",
    "    \"--image_path=\" + colmap_image_path,\n",
    "    \"--ImageReader.camera_model=PINHOLE\",\n",
    "    \"--ImageReader.single_camera=1\",\n",
    "]))"
   ],
   "id": "b391f9b44c59327b",
   "outputs": [],
   "execution_count": null
  },
  {
   "metadata": {},
   "cell_type": "code",
   "source": [
    "import sqlite3\n",
    "colmap_db = sqlite3.connect(colmap_db_path)\n",
    "def select_image(image_name: str):\n",
    "    cur = colmap_db.cursor()\n",
    "    try:\n",
    "        return cur.execute(\"SELECT image_id, camera_id FROM images WHERE name = ?\", [image_name]).fetchone()\n",
    "    finally:\n",
    "        cur.close()"
   ],
   "id": "db6393ba442a62b8",
   "outputs": [],
   "execution_count": null
  },
  {
   "metadata": {},
   "cell_type": "code",
   "source": [
    "c2w_transform = torch.tensor([\n",
    "    [0, -1, 0, 0],\n",
    "    [1, 0, 0, 0],\n",
    "    [0, 0, 1, 0],\n",
    "    [0, 0, 0, 1],\n",
    "], dtype=torch.double).T\n",
    "RDF_TO_DRB_H = torch.tensor([\n",
    "    [0, 1, 0, 0],\n",
    "    [1, 0, 0, 0],\n",
    "    [0, 0, -1, 0],\n",
    "    [0, 0, 0, 1],\n",
    "], dtype=torch.double)"
   ],
   "id": "cdf070dc85e37fe1",
   "outputs": [],
   "execution_count": null
  },
  {
   "metadata": {},
   "cell_type": "code",
   "source": [
    "colmap_images = {}\n",
    "for colmap_image_name, c2w in colmap_image_name_to_c2w.items():\n",
    "    image_id, _ = select_image(colmap_image_name)\n",
    "    c2w = torch.linalg.inv(RDF_TO_DRB_H) @ c2w @ c2w_transform @ RDF_TO_DRB_H\n",
    "    w2c = torch.linalg.inv(c2w)\n",
    "    \n",
    "    colmap_images[image_id] = colmap.Image(\n",
    "        image_id,\n",
    "        qvec=colmap.rotmat2qvec(w2c[:3, :3].numpy()),\n",
    "        tvec=w2c[:3, 3].numpy(),\n",
    "        camera_id=1,\n",
    "        name=colmap_image_name,\n",
    "        xys=np.array([], dtype=np.float64),\n",
    "        point3D_ids=np.asarray([], dtype=np.int64),\n",
    "    )"
   ],
   "id": "f618337561a68897",
   "outputs": [],
   "execution_count": null
  },
  {
   "metadata": {},
   "cell_type": "code",
   "source": [
    "def array_to_blob(array):\n",
    "    return array.tostring()\n",
    "def update_camera_params(camera_id: int, params: np.ndarray):\n",
    "    cur = colmap_db.cursor()\n",
    "    try:\n",
    "        cur.execute(\"UPDATE cameras SET params = ? WHERE camera_id = ?\", [\n",
    "            array_to_blob(params),\n",
    "            camera_id,\n",
    "        ])\n",
    "        colmap_db.commit()\n",
    "    finally:\n",
    "        cur.close()\n",
    "colmap_camera_params = np.asarray([\n",
    "    dataparser_outputs.train_set.cameras.fx[0].item(),\n",
    "    dataparser_outputs.train_set.cameras.fy[0].item(),\n",
    "    dataparser_outputs.train_set.cameras.cx[0].item(),\n",
    "    dataparser_outputs.train_set.cameras.cy[0].item(),\n",
    "])\n",
    "update_camera_params(1, colmap_camera_params)\n",
    "colmap_cameras = {1: colmap.Camera(\n",
    "    id=1,\n",
    "    model=\"PINHOLE\",\n",
    "    width=dataparser_outputs.train_set.cameras.width[0].int().item(),\n",
    "    height=dataparser_outputs.train_set.cameras.height[0].int().item(),\n",
    "    params=colmap_camera_params,\n",
    ")}"
   ],
   "id": "f9986ef2cc58282f",
   "outputs": [],
   "execution_count": null
  },
  {
   "metadata": {},
   "cell_type": "code",
   "source": "colmap_db.close()",
   "id": "9123949fdbd7805f",
   "outputs": [],
   "execution_count": null
  },
  {
   "metadata": {},
   "cell_type": "code",
   "source": [
    "sparse_manually_model_dir = os.path.join(colmap_output_path, \"sparse_manually\")\n",
    "os.makedirs(sparse_manually_model_dir, exist_ok=True)\n",
    "colmap.write_images_binary(colmap_images, os.path.join(sparse_manually_model_dir, \"images.bin\"))\n",
    "colmap.write_cameras_binary(colmap_cameras, os.path.join(sparse_manually_model_dir, \"cameras.bin\"))\n",
    "colmap.write_points3D_binary({}, os.path.join(sparse_manually_model_dir, \"points3D.bin\"))"
   ],
   "id": "b23244aaea4039a4",
   "outputs": [],
   "execution_count": null
  },
  {
   "metadata": {},
   "cell_type": "code",
   "source": [
    "print(\" \\\\\\n    \".join([\n",
    "    \"colmap\",\n",
    "    \"vocab_tree_matcher\",\n",
    "    \"--database_path=\" + colmap_db_path,\n",
    "    \"--VocabTreeMatching.vocab_tree_path=\" + os.path.expanduser(\"~/.cache/colmap/vocab_tree_flickr100K_words256K.bin\"),\n",
    "]))"
   ],
   "id": "8053a5c0c46a4689",
   "outputs": [],
   "execution_count": null
  },
  {
   "metadata": {},
   "cell_type": "code",
   "source": [
    "sparse_dir_triangulated = os.path.join(colmap_output_path, \"sparse\")\n",
    "os.makedirs(sparse_dir_triangulated, exist_ok=True)\n",
    "print(\" \\\\\\n    \".join([\n",
    "    \"colmap\",\n",
    "    \"point_triangulator\",\n",
    "    \"--database_path\", colmap_db_path,\n",
    "    \"--image_path\", colmap_image_path,\n",
    "    \"--input_path\", sparse_manually_model_dir,\n",
    "    \"--output_path\", sparse_dir_triangulated,\n",
    "]))"
   ],
   "id": "9e23eeac1c68ef70",
   "outputs": [],
   "execution_count": null
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 2
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython2",
   "version": "2.7.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
